/*
 * Copyright 2010
 * This file is part of XXXXXX.
 * XXXXXX is free software; you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 * 
 * XXXXXX is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * XXXXXX; if not, write to the Free Software Foundation, Inc., 51 Franklin St,
 * Fifth Floor, Boston, MA 02110-1301 USA
 */
package PD.modules.simulation.Body;

import PD.data.NetworkDataset;
import PD.data.Row;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

/**
 * @author bicha
 */
public class Chromosome {

        private NetworkDataset dataset;
        private double weight = 0;
        private List<Row> NodesSum;
        private List<Row> EdgesProduct;
        private int count = 0;
        private List<Row> removedNodes;

        public Chromosome(NetworkDataset dataset) {
                this.dataset = dataset;
                this.removedNodes = new ArrayList<Row>();
                Chromosome.removeDuplicates(dataset.getEdgesRows());
                Chromosome.removeDuplicates(dataset.getNodesRows());
                this.calculateWeight();
        }

        @Override
        public Chromosome clone() {
                Chromosome newBug = new Chromosome(this.dataset.clone());
                return newBug;
        }

        public double getWeight() {
                return this.weight;
        }

        public Chromosome(Chromosome mother, Chromosome father, NetworkDataset dataset, int motherRecomb, int fatherRecomb) {
                this.dataset = dataset;
                this.dataset.RemoveDesconnectedNodesAndEdges();
                this.removedNodes = new ArrayList<Row>();
                for (int i = 0; i < motherRecomb; i++) {
                        this.removedNodes.add(mother.getNodesRow(i));
                }

                for (int i = 0; i < fatherRecomb; i++) {
                        this.removedNodes.add(father.getNodesRow(i));
                }
                removeDuplicates(this.removedNodes);
                this.calculateWeight();

        }

        public static <Row> void removeDuplicates(List<Row> list) {
                HashSet hs = new HashSet();
                hs.addAll(list);
                list.clear();
                list.addAll(hs);
        }

        public Row getNodesRow(int index) {
                return this.removedNodes.get(index);
        }

        public NetworkDataset getDataset() {
                for (Row node : this.removedNodes) {
                        this.dataset.removeNodesRow(node);
                }
                this.dataset.RemoveDesconnectedNodesAndEdges();
                this.removedNodes.clear();
                return this.dataset;
        }

        private List<Row> getNodesClone(List<Row> Nodes) {
                List<Row> clone = new ArrayList<Row>();
                for (Row node : Nodes) {
                        if (!this.removedNodes.contains(node)) {
                                clone.add(node.clone());
                        }
                }
                return clone;
        }

        private Row getNodeByID(List<Row> rows, int id) {
                for (Row row : rows) {
                        if (row.getID() == id) {
                                return row;
                        }
                }
                return null;
        }

        private void calculateWeight(List<Row> rows, Row row) {
                if (rows.size() > 0) {
                        if (row == null) {
                                row = rows.get(0);
                        }

                        List<Row> edges = this.dataset.getEdgesfromSourceID(row.getID());
                        if (!this.NodesSum.contains(row)) {
                                this.NodesSum.add(row);
                        }

                        rows.remove(row);

                        for (Row edge : edges) {
                                Row targetNode = this.getNodeByID(rows, edge.getTargetVertexID());
                                Row removedRow = null;
                                if (targetNode != null) {
                                        removedRow = getNodeByID(this.removedNodes, targetNode.getID());
                                        if (targetNode.getID() != row.getID() && removedRow == null) {
                                                if (!this.EdgesProduct.contains(edge)) {
                                                        this.EdgesProduct.add(edge);
                                                }
                                                count++;
                                                if (!this.dataset.getEdgesfromSourceID(targetNode.getID()).isEmpty()) {
                                                        this.calculateWeight(rows, targetNode);
                                                }
                                        }
                                }
                        }
                }

        }

        public int getNumberRemovedNodesRows() {
                return this.removedNodes.size();
        }

        public int getNumberDatasetNodes(){
                return this.dataset.getNumberNodesRows();
        }

        public void mutate(int index) {
                Row mutatedNode = this.dataset.getNodesRow(index);
                if (!this.removedNodes.contains(mutatedNode)) {
                        this.removedNodes.add(mutatedNode);
                }
                calculateWeight();
        }

        private void calculateWeight() {
                this.weight = 0;
                this.count = 0;

                List<Row> cloneNodes = this.getNodesClone(this.dataset.getNodesRows());

                double maxScore = 0;
                List<Row> bestNodes = new ArrayList<Row>();
                List<Row> bestEdges = new ArrayList<Row>();
                while (cloneNodes.size() > 0) {
                        //Calculate score
                        this.EdgesProduct = new ArrayList<Row>();
                        this.NodesSum = new ArrayList<Row>();

                        this.calculateWeight(cloneNodes, null);

                        Chromosome.removeDuplicates(NodesSum);
                        Chromosome.removeDuplicates(EdgesProduct);
                        double sum = 0, product = 1;
                        for (Row node : this.NodesSum) {
                                sum += node.getWeight();
                        }
                        for (Row edge : this.EdgesProduct) {
                                product *= edge.getWeight();
                        }
                        double score = Math.abs(sum * product);

                        //Get the subnetwork with maximum score
                        if (score > maxScore /*&& NodesSum.size() > 5*/) {
                                maxScore = score;
                                bestNodes.clear();
                                bestEdges.clear();
                                for (Row node : NodesSum) {
                                        try {
                                                bestNodes.add(node);
                                        } catch (NullPointerException e) {
                                        }
                                }

                                for (Row edge : EdgesProduct) {
                                        try {
                                                bestEdges.add(edge);

                                        } catch (NullPointerException e) {
                                        }
                                }
                                this.weight = maxScore;
                        }
                }

                for (Row node : dataset.getNodesRows()) {
                        if (this.getNodeByID(bestNodes, node.getID()) == null) {
                                this.removedNodes.add(node);
                        }
                }

        }
}
